package com.tanhua.server.service;

import com.alibaba.fastjson.JSON;
import com.tanhua.autoconfig.template.FaceTemplate;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.autoconfig.template.SmsTemplate;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.commons.utils.JwtUtils;
import com.tanhua.dubbo.api.db.UserApi;
import com.tanhua.dubbo.api.db.UserInfoApi;
import com.tanhua.dubbo.api.mongo.FriendApi;
import com.tanhua.dubbo.api.mongo.RecommendUserApi;
import com.tanhua.dubbo.api.mongo.UserLikeApi;
import com.tanhua.dubbo.api.mongo.VisitorApi;
import com.tanhua.model.db.LikeCounts;
import com.tanhua.model.db.User;
import com.tanhua.model.db.UserInfo;
import com.tanhua.model.mongo.Friend;
import com.tanhua.model.mongo.UserLike;
import com.tanhua.model.mongo.Visitors;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.MainCountVo;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.UserInfoVo;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.joda.time.DateTime;
import org.joda.time.Years;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 用户注册-完善个人（保存用户信息、上传头像）-业务实现类
 */
@Service
@Slf4j
public class UserInfoService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    @Autowired
    private FaceTemplate faceTemplate;

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private SmsTemplate smsTemplate;

    @DubboReference
    private UserApi userApi;

    @DubboReference
    private FriendApi friendApi;

    @DubboReference
    private UserLikeApi userLikeApi;

    @DubboReference
    private VisitorApi visitorApi;

    @DubboReference
    private RecommendUserApi recommendUserApi;

    /**
     * 第二步：用户注册：保存用户信息
     *
     * @param userInfoVo
     */
    public void loginReginfo(UserInfoVo userInfoVo) {
        //3.如果登录，则调用服务提供者 保存用户信息
        UserInfo userInfo = new UserInfo();
        //将相同属性 相同类型 的数据从一个对象 copy 另一个对象中
        BeanUtils.copyProperties(userInfoVo, userInfo);
        userInfo.setId(UserHolder.getUserId()); //当前登录用户id 跟 tb_userInfo表 一对一
        userInfo.setAge(getAge(userInfoVo.getBirthday()));//设置年龄
        userInfoApi.add(userInfo);
    }

    /**
     * 根据请求头中token获取user对象
     * @param token
     * @return
     */
    /*private User getLoginUserByToken(String token) {
        String tokenKey = "TOKEN_" + token;
        String userStr = stringRedisTemplate.opsForValue().get(tokenKey);
        //2.如果没有登录 抛出异常
        if(StringUtils.isEmpty(userStr)){
            throw new TanHuaException(ErrorResult.loginFail());
        }
        //2.1 根据userStr 转user对象 并获取用户id
        return JSON.parseObject(userStr, User.class);
    }*/


    /**
     * 通过年-月-日获取年龄
     *
     * @param yearMonthDay
     * @return
     */
    private int getAge(String yearMonthDay) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date birthDay = sdf.parse(yearMonthDay);
            Years years = Years.yearsBetween(new DateTime(birthDay), DateTime.now());
            return years.getYears();
        } catch (ParseException e) {
            log.error("获取用户年龄失败!", e);
            return 0;
        }
    }

    /**
     * 第三步：用户注册：上传头像
     */
    public void loginHeadReginfo(MultipartFile headPhoto) {
        try {
            //2.调用百度人脸识别
            boolean faceFlag = faceTemplate.detect(headPhoto.getBytes());
            if (!faceFlag) {
                throw new TanHuaException(ErrorResult.faceError());
            }
            //3.调用阿里云oss
            String filename = headPhoto.getOriginalFilename();//原始文件名称
            String headUrl = ossTemplate.upload(filename, headPhoto.getInputStream()); //调用阿里云服务 返回头像地址
            //4.调用服务提供者 更新头像地址
            UserInfo userInfo = new UserInfo();
            userInfo.setId(UserHolder.getUserId());//当前用户id
            userInfo.setAvatar(headUrl);//头像地址
            userInfoApi.update(userInfo);
        } catch (IOException e) {
            e.printStackTrace();
            throw new TanHuaException(ErrorResult.error());
        }
    }

    /**
     * 用户信息管理-查询用户资料
     */
    public UserInfoVo getUserInfo(Long userID) {
        //1.获取userId
        Long userId = UserHolder.getUserId();
        //2.调用服务提供者 查询用户资料
        if (userID != null) {
            userId = userID;
        }
        UserInfo userInfo = userInfoApi.getUserInfo(userId);
        //3.构造UserInfoVo返回
        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(userInfo, userInfoVo);
        userInfoVo.setId(userId);//设置用户id
        return userInfoVo;
    }

    /**
     * 用户信息管理-更新用户资料
     */
    public void updateUserInfo(UserInfoVo userInfoVo) {
        //2.调用服务提供者 更新用户资料
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(userInfoVo, userInfo);
        userInfo.setId(UserHolder.getUserId());//设置当前用户id 需要根据id更新用户信息
        userInfo.setAge(getAge(userInfoVo.getBirthday()));
        userInfoApi.update(userInfo);
    }

    /**
     * 用户信息管理-更新用户头像
     *
     * @param headPhoto
     */
    public void updateHeader(MultipartFile headPhoto) {

        try {
            //1.获得登录用户Id--userId
            Long userId = UserHolder.getUserId();
            //2.判断用户头像是否是人物头像--调用faceTemplate处理
            byte[] photoBytes = headPhoto.getBytes();
            boolean flag = faceTemplate.detect(photoBytes);
            //2.1 如果识别结果不是人脸 向用户反馈识别异常信息
            if (!flag) {
                throw new TanHuaException(ErrorResult.faceError());
            }
            //2.2识别正常调用阿里云oss保存头像返回图像地址
            String filename = headPhoto.getOriginalFilename();
            InputStream photoInputStream = headPhoto.getInputStream();
            String headUrl = ossTemplate.upload(filename, photoInputStream);
            //3.删除原来阿里云的数据
            //3.1根据userId获取登录用户原头像信息，调用方法删除阿里云原来头像
            UserInfo userInfo1 = userInfoApi.findUserInfoById(userId);

            if (userInfo1 == null) {
                throw new TanHuaException(ErrorResult.error());
            }
            String avatar = userInfo1.getAvatar();
            //3.2头像信息不为空删除阿里云储存的原头像
            if (StringUtils.isNotEmpty(avatar)) {
                ossTemplate.deleteFile(avatar);
            }
            //4.构建UserInfo对象
            UserInfo userInfo = new UserInfo();
            //4.1修改图像信息
            userInfo.setId(userId);
            userInfo.setAvatar(headUrl);
            //4.调用UserInfoApi 更新数据库信息
            userInfoApi.update(userInfo);
        } catch (IOException e) {
            e.printStackTrace();
            //5.抛出系统异常信息
            throw new TanHuaException(ErrorResult.error());
        }
    }

    /**
     * 修改手机号-1.发送短信验证
     * 获取验证码
     */
    public void sendVerificationCode() {
        //1.获取用户手机号-userInfo/user
        //1.1获取此时登录用户信息
        User user = UserHolder.getUser();
        //1.2获得用户手机号信息
        String mobile = user.getMobile();
        //2.构造key值--VALIDATE_CODE_CHANGE_当前登录用户手机号
        String key = "VALIDATE_CODE_CHANGE_" + mobile;
        //2.1如果redis中还有验证码告知用户验证码尚未过期
        String codeStr = stringRedisTemplate.opsForValue().get(key);
        if (codeStr != null) {
            throw new TanHuaException(ErrorResult.duplicate());
        }
        //3.redis中没有验证码生成6位验证码进行验证
        //String code = RandomStringUtils.random(6);
        String code = "123456";
        //4.调用服务，向用户发送验证信息
        smsTemplate.sendSms(mobile, code);
        //5.将验证码放在redis中并设置有效时间3分钟
        stringRedisTemplate.opsForValue().set(key, code, 3, TimeUnit.MINUTES);
    }

    /**
     * 修改手机号 - 2 校验验证码
     */

    public Boolean checkVerificationCode(Map<String, String> param) {
        //0.构造返回参数
        Boolean flag = false;
        //1.获取请求参数内容
        String verificationCode = param.get("verificationCode");
        //2.获取redis中的验证码信息
        //2.1获取登录者的信息
        User user = UserHolder.getUser();
        String mobile = user.getMobile();
        //2.2判断验证码是否为空
        if (StringUtils.isEmpty(verificationCode)) {
            throw new TanHuaException("验证码不能为空");
        }
        //2.2构造查询验证码的key
        String key = "VALIDATE_CODE_CHANGE_" + mobile;
        String codeStr = stringRedisTemplate.opsForValue().get(key);
        //3.判断redis中的值是否和接收到的值相同 如果相同return true
        if (StringUtils.isEmpty(codeStr)) {
            //3.1 redis中无验证码 抛出验证码已经失效
            throw new TanHuaException(ErrorResult.loginError());
        }
        if (verificationCode.equals(codeStr)) {
            flag = true;
            //4.删除redis中的验证信息避免重复验证
            stringRedisTemplate.delete(key);
        } else if (!verificationCode.equals(codeStr)) {

            throw new TanHuaException(ErrorResult.validateCodeError());
        }
        //5.返回查验结果
        return flag;
    }

    /**
     * 修改手机号 - 3 保存
     * 更换手机号需要更新的数据有 tb_user 删除现在redis中已经存在的token 将新生成的token存入redis中
     */
    public void changePhone(Map<String, String> param) {
        String phone = param.get("phone");
        //1.获取现在登录用户
        User user = UserHolder.getUser();
        //1.2获取原来用户的手机号信息
        String userMobile = user.getMobile();
        //2.修改手机号 调用服务更新用户信息
        user.setMobile(phone);

        userApi.update(user);
        //3.将原有的token进行替换
        //3.1调用Jwt生成新的token
        String token = JwtUtils.createToken(user.getId(), phone);
        String tokenKey = "TOKEN_" + token;
        String jsonString = JSON.toJSONString(user);
        stringRedisTemplate.opsForValue().set(tokenKey, jsonString, 3, TimeUnit.DAYS);
        //3.2将redis中存在的原有的token进行删除
        //3.2.1构造key值删除redis中的原来的数据
        String key1 = JwtUtils.createToken(user.getId(), userMobile);
        String keyOr = "TOKEN_" + key1;
        stringRedisTemplate.delete(keyOr);
    }


    /**
     * 互相喜欢，喜欢，粉丝 - 统计
     *
     * @return
     */
    public LikeCounts counts() {
        LikeCounts likeCounts = new LikeCounts();

        Integer friendCount = friendApi.findById(UserHolder.getUserId());
        likeCounts.setEachLoveCount(friendCount); //互相喜欢

        Integer userLikeCount = userLikeApi.findById(UserHolder.getUserId());
        likeCounts.setLoveCount(userLikeCount);  //喜欢

        Integer fanCount = userLikeApi.findByIds(UserHolder.getUserId());
        likeCounts.setFanCount(fanCount);   //粉丝
        return likeCounts;
    }

    /**
     * 互相喜欢、喜欢、粉丝、谁看过我 - 翻页列表
     */
    public PageResult<MainCountVo> mainCount(Integer type, Long page, Long pageSize, String nickname) {
        //1.构建分页查询
        Long userId = UserHolder.getUserId();
        //2.查询好友分页查询
        List<Long> idList = new ArrayList<>();
        PageResult pageResult = new PageResult<>();

        List<MainCountVo> voList = new ArrayList<>();

            switch (type){
                case 1:
                    idList = friendApi.findByUserId(userId);
                    break;
                case 2:
                    idList = userLikeApi.findByUserId1(userId);
                    break;
                case 3:
                    idList = userLikeApi.findByUserId2(userId);
                    break;
                case 4:
                    idList = visitorApi.findByUserId(userId);
                    break;
            }
            Long counts = Long.valueOf(idList.size());
            Long start = pageSize*(page-1);
            if (page == 2){
                start = 0L;
            }

            if (counts > start) {

                List<Long> pageIdList = idList.stream().skip(start).limit(pageSize).collect(Collectors.toList());

                UserInfo userInfo = new UserInfo();
                userInfo.setNickname(nickname);

                Map<Long, UserInfo> infoMap = userInfoApi.findByIds(pageIdList, userInfo);
                for (Long id : idList) {
                    MainCountVo mainCountVo = new MainCountVo();
                    UserInfo userInfo1 = infoMap.get(id);
                    if (userInfo1 != null){
                        BeanUtils.copyProperties(userInfo1, mainCountVo);
                        Integer score = recommendUserApi.findScore(id, userId);
                        Boolean isLike = userLikeApi.findAlreadyLove(id, userId);
                        mainCountVo.setMatchRate(score);
                        mainCountVo.setAlreadyLove(isLike);
                        voList.add(mainCountVo);
                    }
                }
            }

        return new PageResult(page, pageSize, pageResult.getCounts(), voList);
    }
}
